#ifndef HEAPALLOCATOR_H_INCLUDED
#define HEAPALLOCATOR_H_INCLUDED

#include "Atomic.h"

namespace CEngine
{

#define TRACKING_UNIT U64

struct AllocationHeader {
    U64 size;
};

namespace MemoryManager {
    void initialize(void);
}

class HeapAllocator {
    public:

        ///Creates a heap of the given size using a 4KB page size.
        ///HEAPSIZE MUST BE A MULTIPLE OF PAGESIZE.
        //HeapAllocator(U64 HeapSize);

        ///Creates a heap of the given size with a 4KB/custom page size.
        ///HEAPSIZE MUST BE A MULTIPLE OF PAGESIZE.
        HeapAllocator(U64 HeapSize, U64 PageSize = 4096);

        ///Deletes the heap.
        ~HeapAllocator(void);

        ///returns a pointer to one or more contiguous pages of memory.
        ///RETURNS NULL IF ALLOCATION FAILED.
        void*
        alloc(U64 bytes);

        ///returns an aligned pointer to one or more contiguous pages of memory.
        ///RETURNS NULL IF ALLOCATION FAILED.
        void*
        alignedAlloc(U64 bytes, U64 alignment);

        ///frees one or more contiguous pages of memory depending on allocation.
        void
        free(void* pointer);

        ///returns number of free bytes.
        U64
        getFreeBytes(void);

        ///returns number of free pages.
        U64
        getFreePages(void);

        ///returns number of total bytes.
        U64
        getTotalBytes(void);

        ///returns number of total pages.
        U64
        getTotalPages(void);

        ///returns page size.
        U64
        getPageSize(void);

        ///resets the heap.
        void
        clear(void);

        ///DEBUG FUNCTION.
        ///ONLY USE IF YOU MUST.
        ///RETURNS A POINTER TO THE TRACKING UNIT ARRAY.
        ///LENGTH IS SET TO THE NUMBER OF USED UNITS.
        TRACKING_UNIT*
        debugReturnTUArray(U64& length);

        ///DEBUG FUNCTION.
        ///ONLY USE IF YOU MUST.
        ///RETURNS THE ALLOCATION HEADER FOR A GIVEN ADDRESS.
        AllocationHeader
        debugReturnHeader(void* pointer);

        friend void MemoryManager::initialize(void);

    private:

        void*               heapPointer;
        TRACKING_UNIT*      trackingUnitArray;
        AllocationHeader*   headerArray;
        U64                 pageSize;
        U64                 freePages, totalPages;
};

inline U64
HeapAllocator::getFreeBytes(void) {
    return freePages * pageSize;
}

inline U64
HeapAllocator::getFreePages(void) {
    return freePages;
}

inline U64
HeapAllocator::getTotalBytes(void) {
    return totalPages * pageSize;
}

inline U64
HeapAllocator::getTotalPages(void) {
    return totalPages;
}

inline U64
HeapAllocator::getPageSize(void) {
    return pageSize;
}

}

#endif // HEAPALLOCATOR_H_INCLUDED
